package ca.usask.cs.decisiontree.XMLHandler;

import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * This class reads the given xml file and passes each element to XMLController
 * Referenced from http://www.totheriver.com/learn/xml/xmltutorial.html
 * @author tonyleung
 */
public class LoadXML {

	Document dom;
    XMLController controller;


	public LoadXML(XMLController theController)
    {
        this.controller = theController;
	}


	public void setup(String xmlFile){
		//get the factory
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

		try {
			//Using factory get an instance of document builder
			DocumentBuilder db = dbf.newDocumentBuilder();
			//parse using builder to get DOM representation of the XML file
			dom = db.parse(xmlFile);

		}catch(ParserConfigurationException pce) {
			pce.printStackTrace();
		}catch(SAXException se) {
			se.printStackTrace();
		}catch(IOException ioe) {
			ioe.printStackTrace();
		}
	}


    /**
     * grabs a DecisionTree element and calls the parseDecisionTree method
     */
	public void parse()
    {
		//get the decisionTree elememt
		Element docEle = dom.getDocumentElement();
        //get a nodelist of <DecisionTree> elements
		NodeList treeList = docEle.getElementsByTagName("DecisionTree");

        if(treeList != null && treeList.getLength() > 0)
        {
			for(int i = 0 ; i < treeList.getLength();i++)
            {
				Element tree = (Element)treeList.item(i);
                parseDecisionTree(tree);

			}
		}
	}

    /**
     * grabs the information needed to create a decisionTree
        * then grabs the root node in the nodeList and send the nodeList to
        * parseTreeNodes
     * @param tree
     */
    private void parseDecisionTree(Element tree)
    {
        String treeType="";
        String treeName="";
        int rootNodeID = -1;
        int treeID=0;
        Element rootNode = null;
        NodeList nodeList = null;
        NodeList edgeList = null;
        
        getDecisionTreeInfo(tree,treeType,treeName,rootNodeID,treeID,rootNode,nodeList,edgeList);
        
    }
    public void getDecisionTreeInfo(Element tree,String treeType,String treeName,int rootNodeID,int treeID,Element rootNode, NodeList nodeList,NodeList edgeList){
    	treeType = tree.getAttribute("type");
        treeName = getTextValue(tree,"treeName");
        treeID = getIntValue(tree, "treeID");
   
        //get a nodelist of <TreeNode> elements
        nodeList = tree.getElementsByTagName("TreeNode");
        //get a nodelist of <Edge> elements
        edgeList = tree.getElementsByTagName("Edge");

        if(nodeList != null && nodeList.getLength() > 0)
        {
            //get the RootNode element
            rootNode = (Element)nodeList.item(0);
            rootNodeID = getIntValue(rootNode, "treeNodeID");
        }

        //send the nodeList to parseTreeNodes
        parseTreeNodes(nodeList);
        //send the edgeList to parseEdges
        parseEdges(edgeList);

        // calls the create DecisionTree method in the controller with the
        // Decision tree information
        this.controller.createDecisionTree(treeType, treeID, treeName, rootNodeID);
    }
    public void parseTreeNodes(NodeList treeNodes)
    {
        Element curNode = null;
        String curType = "";
        int curID = 0;
        double curTime = 0.0;
        String curVarName = null;
        double curTermValue = -1;
        boolean curSelected = false;
        
        System.out.println("it works");
        
        if(treeNodes != null && treeNodes.getLength() > 0)
        {
        	getTreeNodeInfo(treeNodes,curNode,curType,curID,curTime,curVarName,curTermValue,curSelected);
        }
    }
    
    public void getTreeNodeInfo(NodeList treeNodes,Element curNode,String curType,int curID,double curTime,String curVarName,double curTermValue,boolean curSelected)
    {
    	// note that this code currently ignores "convenience" attributes 
    	// output as part of the tree (e.g. the name & id of the best edge)
    	
    	for(int i = 0 ; i < treeNodes.getLength();i++)
        {
            //get the RootNode element   		
            curNode = (Element)treeNodes.item(i);
            curType = curNode.getAttribute("type");
            curID = getIntValue(curNode, "treeNodeID");      
            curTime = getDoubleValue(curNode, "time");
            curVarName = getTextValue(curNode, "variableName");
            curSelected = getBooleanValue(curNode, "selected");
            curTermValue = getDoubleValue(curNode, "terminalValue");
 
            this.controller.createTreeNode(curType, curID, curTime, curVarName, curTermValue, curSelected);
        }
    }
    
    public void parseEdges(NodeList edges)
    {

        Element curEdge = null;
        String curType = "";
        int curID = -1;
        double curValue = -1;
        double curProb = 0.0;
        boolean curSelected = false;
        int curSourceID = -1;
        int curDestID = -1;
        
        if(edges != null && edges.getLength() > 0)
        {
        	// TODO the below requires refactoring.  It is very unclear why nonsense values are being passed to 
        	// the following function, only be to overwritten in the function 
        	 getTreeEdgeInfo(edges,curEdge,curType,curID,curValue,curProb,curSelected,curSourceID,curDestID);
        }
    }
    public void getTreeEdgeInfo(NodeList edges,Element curEdge,String curType,int curID,double curValue,double curProb,boolean curSelected,int curSourceID,int curDestID)
    {
    	// note that this ignores the parameter values for many of the parameters!\
    	// TODO refactor.  
    	 for(int i = 0 ; i < edges.getLength();i++)
         {
             //get the RootNode element
             curEdge = (Element)edges.item(i);
             curType = curEdge.getAttribute("type");
             curID = getIntValue(curEdge, "edgeID");
             System.out.println("this is edge id"+i+"       "+curID);
             curValue = getDoubleValue(curEdge, "value");
             curProb = getDoubleValue(curEdge, "probability");
             curSelected = getBooleanValue(curEdge, "selected");
             curSourceID = getIntValue(curEdge, "sourceNodeID");
             curDestID = getIntValue(curEdge, "destinationNodeID");
             String name = getTextValue(curEdge, "edgeName");
             
             this.controller.createEdge(curType, curID, curValue, curProb, curSelected, curSourceID, curDestID,name);
         }
    }
    /**
	 * I take a xml element and the tag name, look for the tag and get
	 * the text content
	 * i.e for <employee><name>John</name></employee> xml snippet if
	 * the Element points to employee node and tagName is name I will return John
	 * @param ele
	 * @param tagName
     * @author http://www.totheriver.com/learn/xml/xmltutorial.html
	 * @return
	 */
	private String getTextValue(Element ele, String tagName)
    {
		NodeList nl = ele.getElementsByTagName(tagName);
		if(nl != null && nl.getLength() > 0) {
			Element el = (Element)nl.item(0);
			org.w3c.dom.Node nodeTextOrNull = el.getFirstChild();
			if (nodeTextOrNull == null)
				return null;					// if no internal text, return null
			else
			{
				String textVal = nodeTextOrNull.getNodeValue();
				return textVal.trim();
			}
		}
		else
			return null;
	}

    /**
	 * Calls getTextValue and returns a int value
	 * @param ele
	 * @param tagName
     * @reference http://www.totheriver.com/learn/xml/xmltutorial.html
	 * @return
	 */
	private int getIntValue(Element ele, String tagName)
    {
        try
        {
            // attempts to find the solution
            return Integer.parseInt(getTextValue(ele,tagName).trim());
        }
        catch(Exception e)
        {
            return Integer.MIN_VALUE;
        }

	}

        /**
	 * Calls getTextValue and returns a int value
	 * @param ele
	 * @param tagName
     * @reference http://www.totheriver.com/learn/xml/xmltutorial.html
	 * @return
	 */
	private boolean getBooleanValue(Element ele, String tagName)
    {
        try
        {
            //in production application you would catch the exception
            return Boolean.parseBoolean(getTextValue(ele,tagName).trim());
        }
        catch(Exception e)
        {
            return false;
        }
	}


    /**
	 * Calls getTextValue and returns a double value
	 * @param ele
	 * @param tagName
     * @reference http://www.totheriver.com/learn/xml/xmltutorial.html
	 * @return
	 */
	private double getDoubleValue(Element ele, String tagName)
    {
        try
        {
            //in production application you would catch the exception
            return Double.parseDouble(getTextValue(ele,tagName).trim());
        }
        catch(Exception e)
        {
            return Double.NEGATIVE_INFINITY;
        }
	}
}
